在前面我们学习了 CJS
,ESM
的用法,除了语法的区别外,其实还存在一些其它的区别,
下面将详细介绍一下 加载时机
、导出内容
、文件命名
上的区别。
CJS
支持动态加载模块 (require
语句可以出现在任意位置);ESM
会在所有模块都加载完毕后才执行代码 (通常会将 import
导入语句放在模块的顶部);因此 ESM
可以在代码执行前进行静态分析和优化,从而提高性能 (比如自动移除无用的死代码)。
而 CJS
需要等到代码运行时才能确定依赖关系和加载模块。
下面是一个具体的例子:从2个文件中分别导入1个函数,然后执行这2个导入的函数
。
```js // module1 console.log('load module1')
export function helloModule1() { console.log('hello module1') }
// module2 console.log('load module2')
export function helloModule2() { console.log('hello module2') }
// index.js import { helloModule1 } from './module1.js'
helloModule1()
import { helloModule2 } from './module2.js'
helloModule2() ```
```js // module1 console.log('load module1')
function helloModule1() { console.log('hello module1') }
module.exports = { helloModule1 }
// module2 console.log('load module2')
function helloModule2() { console.log('hello module2') }
module.exports = { helloModule2 }
// index.js const { helloModule1 } = require('./module1.js')
helloModule1()
const { helloModule2 } = require('./module2.js')
helloModule2() ```
执行结果如下。
ES Modules (ESM) 和 CommonJS (CJS) 在导入模块的对象引用上有不同的行为。
在 ESM 中,当我们导入一个变量时,实际上是导入了该变量的引用。这意味着,如果导出的变量在导入模块中发生了改变,导入的变量也会随之改变。
而在 CommonJS 中,导入的是导出模块的值的拷贝,而不是引用。这意味着,即使导出模块中的值发生了改变,导入模块中导入的变量不会受到影响。
简而言之,ESM 导入的是值的引用,而 CJS 导入的是值的拷贝。
这是两者在模块导入中的一个重要区别,下面通过具体的例子感受一下。
```js // public.js export let num = 1
export function update() { num += 1 console.log('update num', num) }
// main.js import { num, update } from './public.js'
console.log(num) update()
console.log(num) update() ```
```js // public.js let num = 1
function update() { num += 1 console.log('update num', num) }
module.exports = { num, update }
// main.js const { num, update } = require('./public.js')
console.log(num) update()
console.log(num) update() ```
通常情况下模块一般都以 .js
结尾,通过 package.json
中 "type":"module"
区分模块类型,
实际上还可以通过文件命名来区分 .cjs
表明是 CJS 规范的模块,.mjs
表明是 ESM 规范的模块。
比如有如下 3 个文件,内容都是如下的 ESM 规范内容。
js
// index.js
// index.cjs
// index.mjs
import _ from 'fs'
console.log('hello')
在没有 package.json
设置 "type":"module"
的前提下,只有 index.mjs
能成功执行。
.js
和 .cjs
都会报错,且会提示 加载ESM资源需要 .mjs 结尾或 "type":"module"
。
本节主要介绍了 CJS 与 ESM 之间的一些区别,包括 加载时机
、导出内容
、文件命名
上的区别:
CJS
支持动态加载模块 (require
语句可以出现在任意位置),ESM
会在所有模块都加载完毕后才执行代码 (通常会将 import 导入语句放在模块的顶部);ESM
导入的是值的引用,而 CJS
导入的是值的拷贝;.js
结尾,通过 package.json
中 "type":"module"
区分模块加载类型,也可以通过文件命名来区分 .cjs
表明是 CJS 规范的模块,.mjs
表明是 ESM 规范的模块。